/*-//GEN-BEGIN:copyright
 * ForBILE - Framework for Building Interactive Learning Environments
 * Computing Institute - Federal University of Alagoas - Brazil
 * Group of Otimization of the Web
 *
 * Copyright (C) 2007 Ig Bittencourt (ig.ibert@gmail.com)
 *		      Camila Nunes (camilapbnunes@hotmail.com)
 *	              Marcos Tadeu (zero.ufal@gmail.com)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *///GEN-END:copyright
package br.ufal.ic.forbile.infra;

import java.util.logging.Logger;
import br.ufal.ic.forbile.infra.emathema.*;
import br.ufal.ic.utils.Configuration;
import edu.stanford.smi.protegex.owl.model.OWLIndividual;
import edu.stanford.smi.protegex.owl.model.query.QueryResults;
import java.util.Map;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Set;

/**
 * GenericKAO.java
 *
 * <p>A full description of this class.
 *
 * @see InfraFacade.
 *
 * @author <a href="mailto:ig.ibert@gmail.com">ig</a>.
 * @since 0.1
 * @version 0.1
 *
 */
public class GenericKAO extends InfraFacade<String, Object> {
    
    // Constants -----------------------------------------------------
    
    private static final String CALL_CREATE      = "create";
    private static final String CALL_ADD         = "add";
    private static final String CALL_GET         = "get";
    private static final String CALL_SET         = "set";
    private static final String CALL_DELETE      = "delete";
    private static final String CALL_REMOVE      = "remove";
    
    // Attributes ----------------------------------------------------
    
    private Class[]     types;
    private Object[]    values;
    
    private Object      instance;
    private Class       clazzOntology;
    private Class       clazzKnowledge;
    
    // Static --------------------------------------------------------
    
    private static Logger logger;
    
    static {
        logger = Logger.getLogger(GenericKAO.class.getName());
    }
    
    // Constructors --------------------------------------------------
    
    /**
     * Creates a new instance of GenericKAO.
     *
     * @param OWL_KEY The ontology key.
     */
    public GenericKAO(final String OWL_KEY) {
        knowledge = KnowledgeFactory.getKnowledge(OWL_KEY,
                Configuration.getInstance().getValue(OWL_KEY) );
    }
    
    // Public --------------------------------------------------------
    
    /**
     * Insert method to ontology instances.
     *
     * @param configuration Map with configuration object that have to be
     * inserted.
     *
     * @throws java.lang.Exception If there are problems in creating
     */
    public void create(final Map<String, Object> configuration)
    throws Exception {
        
        defaultExecution(configuration);
        
        String aux = CALL_CREATE + getClassName(clazzOntology.getName());
        
        // invoke the method register()
        Method method = clazzKnowledge.getMethod(aux, types);
        Object result = method.invoke(instance, values);
        Class r = result.getClass();
        
        Set<String> addMethods = configuration.keySet();
        
        Method[] methods = clazzOntology.getDeclaredMethods();
        
        addMethods.remove("Name");
        addMethods.remove("Individual");
        
        // invoke the method addX()
        for (String s : addMethods) {
            
            types   = new Class[]   { configuration.get(s).getClass() };
            values  = new Object[]  { configuration.get(s) };
            
            s = s.replaceFirst(s.substring(0, 1), s.substring(0, 1).toUpperCase());
            
            for (int i = 0; i < methods.length; i++) {
                // get the type of the parameter
                if ((CALL_SET + s).equals(methods[i].getName())) {
                    
                    types[0] = methods[i].getParameterTypes()[0];
                    logger.info("Type: " + types[0]);
                    
                    // verify if the parameter is an owl instance
                    if ( clazzOntology.getName().substring(0, 5).
                            equals( types[0].toString().substring(10, 15) ) ) {
                        String aux2 = types[0].toString().substring(53, types[0].toString().length());
                        String aux3 = types[0].toString().substring(10, types[0].toString().length());
                        logger.info("aux2" + aux2);
                        logger.info("aux3" + aux3);
                        OWLIndividual owli = retrieve(values[0].toString(), aux2);
                        
                        Class cls = Class.forName(aux3);
                        types[0] = cls;
                        owli.as(cls);
                        values[0] = owli;
                    }
                }
            }
            
            System.out.println(types[0]);
            method = r.getMethod( (CALL_SET + s) , types[0] );
            method.invoke(result, values);
        }
        saveKnowledge();
    }
    
    /**
     * Retrieve a configuration map according to the complete triple according
     * of the semantic web structure.
     *
     * @param subject The complete triple according to the semantic web
     *  structure.
     * @param predicate The complete triple according to the semantic web
     *  structure.
     * @param object the complete triple according to the semantic web
     *  structure.
     *
     * @throws java.lang.Exception If there are some update problem.
     *
     * @return The configuration map.
     */
    public Map retrieve(String subject, String predicate,
            String object) throws Exception {
        
        if ( (subject == null && predicate == null && object == null )
        || ( subject != null && predicate != null && object != null )){
            try {
                throw new Exception("Invalid Configuration");
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        
        String queryString = null;
        
        String auxSubject   = "";
        String auxPredicate = "";
        String auxObject    = "";
        
        if ( subject == null ){
            auxSubject = " ?subject ";
            subject = auxSubject;
        }
        
        if ( predicate == null ){
            auxPredicate = " ?predicate ";
            predicate = auxPredicate;
        }
        
        if ( object == null ){
            auxObject = " ?object ";
            object = auxObject;
        }
        
        queryString = "SELECT " + auxSubject + auxPredicate + auxObject +
                "  WHERE { " + subject + predicate + object + " }";
        
        Map map = new HashMap();
        System.out.println("queryString: " + queryString);
        QueryResults qr = ((EMATHEMAKnowledge) knowledge).executeQuery(queryString);
        
        int i = 0;
        
        while (qr.hasNext()){
            i++;
            Map aux = qr.next();
            map.put(i,aux.get("subject"));
        }
        
        return map;
        
    }
    
    public Map retrieve(String query) throws Exception {
        
        if (query == null){
            try {
                throw new Exception("Invalid Configuration");
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        
        String queryString = query;
        
        QueryResults qr = null;
        Map map = new HashMap();
        
        try {
            qr = ((EMATHEMAKnowledge) knowledge).executeQuery(queryString);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        
        int i = 0;
        
        while (qr.hasNext()){
            i++;
            Map aux = qr.next();
            map.put(i,aux.get("subject"));
        }
        
        return map;
        
    }
    /**
     * Retrieve an individual ontology.
     *
     * @param individual
     * @param _class
     *
     * @throws java.lang.Exception If there are some update problem.
     *
     * @return The individual ontology.
     */
    public OWLIndividual retrieve(final String individual, final String _class)
    throws Exception {
        
        if ( individual == null || _class == null ){
            try {
                throw new Exception("Invalid Configuration");
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        
        types  = new Class[]   {String.class};
        values = new Object[]  {individual};
        
        instance = knowledge.getOntologyFactory();
        
        clazzKnowledge = instance.getClass();
        
        String aux  = (CALL_GET + _class.replaceFirst(_class.substring(0, 1),
                _class.substring(0, 1).toUpperCase()) );
        
        // invoke the method register()
        Method method = clazzKnowledge.getMethod(aux, types);
        Object result = method.invoke(instance, values);
        
        return (OWLIndividual) result;
        
    }
    
    /**
     * Update the ontology instances.
     *
     * @param configuration Map with configuration object that have to be
     * update.
     *
     * @throws Exception If there are some update problem.
     */
    public void update(final Map<String, Object> configuration)
    throws Exception {
        
        delete(configuration);
        create(configuration);
    }
    
    /**
     * Delete the ontology instances.
     *
     * @param configuration Map with configuration object that have to be
     * delete.
     *
     * @throws Exception If there are some delete problem.
     */
    public void delete(final Map<String, Object> configuration)
    throws Exception {
        
        defaultExecution(configuration);
        
        String aux = CALL_CREATE + getClassName(clazzOntology.getName());
        
        // invoke the method register()
        Method method = clazzKnowledge.getMethod(aux, types);
        Object result = method.invoke(instance, values);
        
        Class r = result.getClass();
        
        // invoke the method delete
        types   = new Class[]   {};
        values  = new Object[]  {};
        
        method = r.getMethod( (CALL_DELETE) , types);
        method.invoke(result, values);
        
        saveKnowledge();
    }
    
    // Z implementation ----------------------------------------------
    
    // Y overrides ---------------------------------------------------
    
    // Package protected ---------------------------------------------
    
    // Protected -----------------------------------------------------
    
    // Private -------------------------------------------------------
    
    /**
     * Default execution to configuration map.
     *
     * @param configuration Map with configuration object that have to be
     * executed.
     */
    private void defaultExecution(final Map<String, Object> configuration) {
        if (!(configuration.containsKey("Name") || configuration.containsKey("Individual"))) {
            try {
                throw new Exception("Invalid Configuration");
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        
        // Retrieve the Class Name and URI from configuration Map
        clazzOntology = (Class) configuration.get("Name");
        
        types  = new Class[]   {String.class};
        values = new Object[]  {configuration.get("Individual")};
        
        instance = knowledge.getOntologyFactory();
        
        clazzKnowledge = instance.getClass();
    }
    
    /**
     * Save a knowledge.
     */
    private void saveKnowledge(){
        knowledge.save();
    }
    
    private String getClassName(final String _class){
        if (_class.contains(".")) {
            int lastIndex = _class.lastIndexOf(".");
            return _class.substring(lastIndex + 1);
        } else {
            return _class;
        }
    }
    
    // Inner classes -------------------------------------------------
    
}
